feat: Add filtering to button dropdown#4637
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4637 +/- ##
==========================================
+ Coverage 97.57% 97.59% +0.02%
==========================================
Files 948 950 +2
Lines 30517 30642 +125
Branches 11168 11229 +61
==========================================
+ Hits 29777 29906 +129
+ Misses 693 689 -4
Partials 47 47 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
0d5d891 to
14e5718
Compare
14e5718 to
b0fd947
Compare
042705d to
50e2c55
Compare
50e2c55 to
d5f7211
Compare
edb620f to
2aeec5f
Compare
2aeec5f to
85a8449
Compare
| expect(footer!.getElement()).toHaveTextContent('No items found'); | ||
| }); | ||
|
|
||
| test('counts leaf items in groups for totalCount', () => { |
There was a problem hiding this comment.
This is inconsistent with multiselect - that does include groups to filtering results text. Most importantly, the filtering also works inconsistently: in multiselect one can filter for the group name, e.g. "Group 1" - it then shows all items that belong to this group. Why is this different in the button dropdown? E.g. in the demo I type "Edit" to the filter to find all items that belong to the "Edit" category. The result is empty though.
There was a problem hiding this comment.
Oh, good callout. I don't know what the intended behavior should be here actually, need to confirm with design. We had this behavior implicltly from the original prototype and signoff but I don't know if this was ever discussed.
But I would imagine behaving like select/multiselect could be pretty unhelpful here, especially for really large dropdowns (like with EC2), where typing in "instance" would get you an really big list of items rather than a specific action you were maybe looking for.
Let's assume this is correct for now, but I won't merge until I get an approval on this specific behavior from the designer.
There was a problem hiding this comment.
I think this can be misleading. E.g. when I search for "Security" in your demo - it matches one item our of two that are defined inside the "Security" group - because that one happens to have "security" keyword in the description. It is true that matching groups can result in many items in the output - but this can be mitigated by clarifying the input, e.g. saying "change security" or "security groups" would then find the item alone, no longer matching every item in the group.
There was a problem hiding this comment.
The trouble is also that it's not consistent. For nested expandable groups (for example: "Networking" in the nested expandable group demo in the filtering dev page), the inner group name is intentionally discarded when filtering, because we don't want to have nested flattened groups. So the outer group name is filterable, but the inner one isn't. For example, typing in "Diagnostics" in the nested demo gets you no results. The alternative is to match the items under the nested group, but not show the parent group, which makes it equally confusing why they were matched.
There was a problem hiding this comment.
Had a chat with the designer and decided to keep this behavior.
- This (group filtering) wasn't part of the initial scope internal or in discussion with the building team. Adding this (especially having to re-address the group "collapsing" topic we already had an agreement on earlier) is going to expand the scope for no clear use-case.
- The component is different to select and multiselect; the use-case of getting to specific actions without digging through menus is different to the select/multiselect components more "exploratory" use-case.
- Because the API for this is pretty opaque, we could treat this as a two-way door to introduce in the future if we really had strong data pushing us to do so. And this ("auto" filtering) is just a stepping stone to supporting more complex filtering in the future.
| ); | ||
| }); | ||
|
|
||
| test('passes filterText to renderItem for checkbox items', () => { |
There was a problem hiding this comment.
what is the purpose of checking for different item types now that filtering text is passed as a separate arg?
There was a problem hiding this comment.
Test coverage! This logic is actually duplicated three times, and each test tests a different place where the check is defined.
Not sure how much sense deduplicating it makes, it's just one additional prop in the event handler call.
There was a problem hiding this comment.
Can we have a single test then that defines a button dropdown featuring all item types?
There was a problem hiding this comment.
Sure. But to make sure callback assertions are correct, I still need to trigger each item individually. So not much is deduplicated.
| position?: string; | ||
| renderItem?: ButtonDropdownProps.ItemRenderer; | ||
| filteringText?: string; | ||
| filteringEnabled?: boolean; |
There was a problem hiding this comment.
nit: I would either make filteringText required, or remove filteringEnabled in favour of filteringText !== undefined check.
There was a problem hiding this comment.
Felt it was cleaner this way to have a boolean rather than combining these two attributes into an | undefined union. hasFiltering is easy to determine at the top-level from the filteringType prop, and for child elements, it's easier to pass the boolean straight through to effect dependencies (for example: expandable-category-element.tsx) rather than create a separate variable (const hasFiltering = filteringText !== undefined) just for effects (reversing additional logic in use-button-dropdown.tsx, which would need to set filteringText: hasFiltering ? filteringText : undefined).
Don't feel super strongly about this though, it's just a minor refactor. Let me know if that makes sense or you'd still prefer condensing this into one state variable.
| filteringResultsText: filteredText, | ||
| }); | ||
|
|
||
| // Only create a filteringDescription element if filtering is actually enabled, |
There was a problem hiding this comment.
nit: I think this comment is redundant as the code is clear enough
There was a problem hiding this comment.
The comment describes what the hook does internally; easy to check the source, but I felt it important to call out here, since a seemingly innocent refactor to not match the existence of the element with the existence of the id could create an accessibility issue.
Description
Button dropdown action filtering implemented a similar way to select/multiselect filtering. The big underlying change is that filtering requires the use of
aria-activedescendantrather than simply moving focus from item to item. So most of the internal changes are about disabling focusing logic when filtering is active, so that the real focus can stay on the input (and "accessibility" focus can move around usingaria-activedescendant).Rel: AWSUI-61960 (Chorus: qodG8KXdJXXk)
How has this been tested?
Updated unit tests, a bit of manual accessibility testing, but more validation with other screen readers is needed.
Review checklist
The following items are to be evaluated by the author(s) and the reviewer(s).
Correctness
CONTRIBUTING.md.CONTRIBUTING.md.Security
checkSafeUrlfunction.Testing
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.